﻿using System;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using PropertyExpression.ControllableQuery.Test;
using PropertyExpression.ControllableQuery.Toolkit.Test.Queries;

namespace PropertyExpression.ControllableQuery.Toolkit.Test
{
    [TestFixture]
    public class MainTest
    {
        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"System.Data.SqlClient.SqlException occurred
Message = 'Invalid column name 'LanguageCode2'.'
QueryType = 'PropertyExpression.ControllableQuery.Toolkit.Test.Queries.Error1Query'
LineNumber = '32'
SqlCommandTextWithLineNumbers = 
  1:SELECT
  2:    BillOfMaterialsLine.PartNumber
  3:FROM
  4:    BillOfMaterialsLine
  5:WHERE
  6:    BillOfMaterialsHeaderId = @p0
  7:    AND (
  8:         (
  9:          BillOfMaterialsLine.MainLinkExcludedFrom = 0
 10:          AND EXISTS ( SELECT
 11:                        1
 12:                       FROM
 13:                        MainPartLink
 14:                       WHERE
 15:                        MainPartLink.PartNumber = BillOfMaterialsLine.PartNumber
 16:                        AND MainPartLink.ModelPublicationId = @p1
 17:                        AND MainPartLink.ServicePartsPageId <> @p2 )
 18:         )
 19:         OR (
 20:             BillOfMaterialsLine.InnerLinkExcludedFrom = 0
 21:             AND EXISTS ( SELECT
 22:                            1
 23:                          FROM
 24:                            InnerPartLink
 25:                            INNER JOIN Model
 26:                                ON InnerPartLink.ModelCode = Model.ModelCode
 27:                                   AND InnerPartLink.ModelSequence = Model.ModelSequence
 28:                            INNER JOIN Publication
 29:                                ON InnerPartLink.PublicationId = Publication.PublicationId
 30:                            INNER JOIN LocalizedPublication
 31:                                ON InnerPartLink.PublicationId = LocalizedPublication.PublicationId
*32:                                   AND LocalizedPublication.LanguageCode2  = @p3
 33:                          WHERE
 34:                            InnerPartLink.PartNumber = BillOfMaterialsLine.PartNumber
 35:                            AND Model.ModelId = @p4
 36:                            AND Publication.FollowingPublicationId IS NULL
 37:						)
 38:            )
 39:        )
SqlCommandText = 
SELECT
    BillOfMaterialsLine.PartNumber
FROM
    BillOfMaterialsLine
WHERE
    BillOfMaterialsHeaderId = @p0
    AND (
         (
          BillOfMaterialsLine.MainLinkExcludedFrom = 0
          AND EXISTS ( SELECT
                        1
                       FROM
                        MainPartLink
                       WHERE
                        MainPartLink.PartNumber = BillOfMaterialsLine.PartNumber
                        AND MainPartLink.ModelPublicationId = @p1
                        AND MainPartLink.ServicePartsPageId <> @p2 )
         )
         OR (
             BillOfMaterialsLine.InnerLinkExcludedFrom = 0
             AND EXISTS ( SELECT
                            1
                          FROM
                            InnerPartLink
                            INNER JOIN Model
                                ON InnerPartLink.ModelCode = Model.ModelCode
                                   AND InnerPartLink.ModelSequence = Model.ModelSequence
                            INNER JOIN Publication
                                ON InnerPartLink.PublicationId = Publication.PublicationId
                            INNER JOIN LocalizedPublication
                                ON InnerPartLink.PublicationId = LocalizedPublication.PublicationId
                                   AND LocalizedPublication.LanguageCode2  = @p3
                          WHERE
                            InnerPartLink.PartNumber = BillOfMaterialsLine.PartNumber
                            AND Model.ModelId = @p4
                            AND Publication.FollowingPublicationId IS NULL
						)
            )
        )
Class = '16'
Number = '207'
State = '1'
ErrorCode = '-2146232060'
Procedure = ''
Server = '(local)'
Source = '.Net SqlClient Data Provider'")]
        public void Error1Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error1Query(
                                    queryContext.CreateGuidParam(),
                                    queryContext.CreateGuidParam(),
                                    queryContext.CreateGuidParam(),
                                    new WithInnerPartLinkSwitcher(
                                        true,
                                        queryContext.CreateStringParam(),
                                        queryContext.CreateGuidParam())
                                    )
                );
        }

        [Test]
        public void Error2Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error2Query(
                                    queryContext.CreateStringParam()
                                    )
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(SqlException), ExpectedMessage = "The variable name '@p1' has already been declared. Variable names must be unique within a query batch or stored procedure.")]
        public void Error3FixQuery()
        {
            ControllableQuery.Test.MainTest.ExecuteReader(
                queryContext => new Error3Query(
                                    queryContext.CreateParam("ModelCode1"),
                                    queryContext.CreateParam("ModelSequence1")
                                    )
                ).ToList();
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "The variable name '@p1' has already been declared. Variable names must be unique within a query batch or stored procedure.", MatchType = MessageMatch.Contains)]
        public void Error3FixQueryQueryChecker()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error3Query(
                                    queryContext.CreateStringParam(),
                                    queryContext.CreateStringParam()
                                    )
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "Cannot find data type IPublicationCodeType2.", MatchType = MessageMatch.Contains)]
        public void Error4Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error4Query(
                                    queryContext.CreateTableValuedParam<IPublicationCodeType2>()
                                    )
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "The type of query result property was incorrect ColumnName = 'PublicationTypeCode'. Expected SQL Server Database Engine type DataType = 'int', AllowDBNull = 'True' but was .NET Framework type 'System.Int32'.", MatchType = MessageMatch.Contains)]
        public void Error8Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error8Query()
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "The type of query result property was incorrect ColumnName = 'PublicationTypeCode'. Expected SQL Server Database Engine type DataType = 'int', AllowDBNull = 'True' but was .NET Framework type 'System.String'.", MatchType = MessageMatch.Contains)]
        public void Error9Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error9Query()
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "The query result type does not contain a definition for property 'PublicationTypeCode'.", MatchType = MessageMatch.Contains)]
        public void Error10Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error10Query()
                );
        }

        private static void CheckQueries<T>(string connectionString, params Func<QueryContext, IQuery<T>>[] queryFuncs)
        {
            QueryChecker.CheckQueries(connectionString, ControllableQuery.Test.QueryCheck.MainTest.QueryResultPropertyTypeChecker, queryFuncs);
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = "The query result does not contain a column 'PublicationTypeCode2'.", MatchType = MessageMatch.Contains)]
        public void Error11Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error11Query()
                );
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"'Must declare the scalar variable ""@p0AND", MatchType = MessageMatch.Contains)]
        public void Error12Query()
        {
            CheckQueries(
                ConnectionString,
                queryContext => new Error12Query(
                                    queryContext.CreateStringParam(),
                                    queryContext.CreateStringParam()
                                    )
                );
        }

        private static void CheckAllQueries(Type type)
        {
            try
            {
                QueryChecker.CheckAllQueries(
                    ConnectionString,
                    ControllableQuery.Test.QueryCheck.MainTest.QueryResultPropertyTypeChecker,
                    ControllableQuery.Test.QueryCheck.MainTest.ParamCreator,
                    ControllableQuery.Test.QueryCheck.MainTest.QueryTypePredicate,
                    ControllableQuery.Test.QueryCheck.MainTest.MethodInfoPredicate,
                    ControllableQuery.Test.QueryCheck.MainTest.ChoiceInvocationGetter,
                    new[] {type}
                    );
            }
            catch (TargetInvocationException e)
            {
                throw e.InnerException;
            }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"Invalid column name 'PublicationTypeId2'", MatchType = MessageMatch.Contains)]
        public void Error13Query()
        {
            CheckAllQueries(typeof(Query1));
        }

        private static class Query1
        {
            public static INonQuery GetQuery()
            {
                return "INSERT PublicationType (PublicationTypeId2) VALUES('')".ToNonQuery();
            }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"Invalid column name 'PublicationTypeId2'", MatchType = MessageMatch.Contains)]
        public void Error14Query()
        {
            CheckAllQueries(typeof(Query2));
        }

        private static class Query2
        {
            public static IQuery<IInfo> GetQuery()
            {
                return "SELECT PublicationTypeId2 FROM PublicationType".ToQuery<IInfo>();
            }

            public interface IInfo { }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"Method is not static", MatchType = MessageMatch.Contains)]
        public void Error15Query()
        {
            CheckAllQueries(typeof(Query3));
        }

        private class Query3
        {
            public INonQuery GetQuery()
            {
                return "INSERT PublicationType (PublicationTypeId2) VALUES('')".ToNonQuery();
            }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"Method is not static", MatchType = MessageMatch.Contains)]
        public void Error16Query()
        {
            CheckAllQueries(typeof(Query4));
        }

        private class Query4
        {
            public IQuery<IInfo> GetQuery()
            {
                return "SELECT PublicationTypeId2 FROM PublicationType".ToQuery<IInfo>();
            }

            public interface IInfo { }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"method is called in a constructor of type", MatchType = MessageMatch.Contains)]
        public void Error17Query()
        {
            CheckAllQueries(typeof(Query5));
        }

        private class Query5
        {
            public Query5()
            {
                "INSERT PublicationType (PublicationTypeId2) VALUES('')".ToNonQuery();
            }
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(QueryCheckException), ExpectedMessage = @"method is called in a constructor of type", MatchType = MessageMatch.Contains)]
        public void Error18Query()
        {
            CheckAllQueries(typeof(Query6));
        }

        private class Query6
        {
            public Query6()
            {
                "SELECT PublicationTypeId2 FROM PublicationType".ToQuery<IInfo>();
            }

            private interface IInfo { }
        }

        private static string ConnectionString
        {
            get { return ControllableQuery.Test.MainTest.ConnectionString; }
        }
    }
}